package org.karelun.gef.utils;

import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.TransformerFactoryConfigurationError;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

public class DOMUtils {
	
	private static class ElementIterator implements Iterator<Element> {

		private NodeList children;

		private String tagName;

		private int index;

		private Element next;

		public ElementIterator(Node parent) {
			this(parent, null);
		}

		public ElementIterator(Node parent, String tagName) {
			this.children = parent.getChildNodes();
			this.tagName = tagName;
			this.index = 0;
			this.next = findNextElement();
		}

		private Element findNextElement() {
			for (int i = index; i < children.getLength(); i++) {
				Node n = children.item(i);
				if (isElementByTag(n, tagName)) {
					next = (Element) n;
					index = i + 1;
					return next;
				}
			}
			return null;
		}

		public boolean hasNext() {
			return next != null;
		}

		public Element next() {
			Element result = next;
			next = findNextElement();
			return result;
		}

		public void remove() {
		}
	}

	public static boolean isElementByTag(Node node, String tagName) {
		if (!(node instanceof Element))
			return false;
		if (tagName == null)
			return true;
		Element element = (Element) node;
		String tag = element.getTagName();
		return tag.equals(tagName)
				|| getLocalName(tag).equals(getLocalName(tagName));
	}

	public static String getLocalName(String qualifiedName) {
		int index = qualifiedName.indexOf(':');
		if (index >= 0)
			return qualifiedName.substring(index + 1);
		return qualifiedName;
	}

	public static Iterator<Element> childElementIter(Node parent) {
		return new ElementIterator(parent);
	}

	public static Element getFirstChildElement(Node parent) {
		return childElementIter(parent).next();
	}

	public static Element[] getChildElements(Node parent) {
		List<Element> list = new ArrayList<Element>(parent.getChildNodes()
				.getLength());
		Iterator<Element> it = childElementIter(parent);
		while (it.hasNext()) {
			list.add(it.next());
		}
		return list.toArray(new Element[list.size()]);
	}

	public static Element[] getChildElementsByTag(Node parent, String tag) {
		List<Element> list = new ArrayList<Element>(parent.getChildNodes()
				.getLength());
		Iterator<Element> it = childElementIterByTag(parent, tag);
		while (it.hasNext()) {
			list.add(it.next());
		}
		return list.toArray(new Element[list.size()]);
	}

	public static Iterator<Element> childElementIterByTag(Node parent,
			String tagName) {
		return new ElementIterator(parent, tagName);
	}

	public static Element createElement(Node parent, String tag) {
		Document doc = parent.getNodeType() == Node.DOCUMENT_NODE ? (Document) parent
				: parent.getOwnerDocument();
		Element element = doc.createElement(tag);
		parent.appendChild(element);
		return element;
	}

	public static void write(Document doc, OutputStream os) {
		Transformer transformer = getDefaultTransformer();
		if (transformer == null)
			return;
		try {
			transformer.transform(new DOMSource(doc), new StreamResult(os));
		} catch (TransformerException e) {
			e.printStackTrace();
		}
	}

	public static DocumentBuilder getDocumentBuilder() {
		DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
		try {
			return factory.newDocumentBuilder();
		} catch (ParserConfigurationException e) {
			e.printStackTrace();
		}
		return null;
	}

	private static Transformer getDefaultTransformer() {
		try {
			return TransformerFactory.newInstance().newTransformer();
		} catch (TransformerConfigurationException e) {
			e.printStackTrace();
		} catch (TransformerFactoryConfigurationError e) {
			e.printStackTrace();
		}
		return null;
	}
}
